package com.ddxcloud.movie.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ddxcloud.common.constants.PayMethodConstant;
import com.ddxcloud.common.constants.PayTypeConstant;
import com.ddxcloud.common.constants.ResultStatusConstant;
import com.ddxcloud.common.constants.enums.OrderStatusEnum;
import com.ddxcloud.common.constants.enums.RefundStatusEnum;
import com.ddxcloud.common.handler.BusinessException;
import com.ddxcloud.common.utils.IpUtil;
import com.ddxcloud.common.utils.PayOrderUtil;
import com.ddxcloud.movie.config.properties.CommonProperties;
import com.ddxcloud.movie.dto.RefundDTO;
import com.ddxcloud.movie.entity.Order;
import com.ddxcloud.movie.entity.PaymentOrder;
import com.ddxcloud.movie.entity.RefundOrder;
import com.ddxcloud.movie.mapper.PaymentOrderMapper;
import com.ddxcloud.movie.mapper.RefundOrderMapper;
import com.ddxcloud.movie.service.OrderService;
import com.ddxcloud.movie.service.RefundOrderService;
import com.ddxcloud.movie.service.WechatRefundService;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
public class WechatRefundServiceImpl implements WechatRefundService {

    @Resource
    PaymentOrderMapper paymentOrderMapper;

    @Resource
    CommonProperties commonProperties;

    @Resource
    RefundOrderService refundOrderService;

    @Resource
    RefundOrderMapper refundOrderMapper;

    @Resource
    OrderService orderService;

    @Resource
    WxPayService wxPayService;

    @Override
    public WxPayRefundV3Result createOrder(RefundDTO dto, HttpServletRequest request) {
        PaymentOrder paymentOrder = paymentOrderMapper.getOrderByOrderId(dto.getOrderId());
        if (ObjectUtil.isNotNull(paymentOrder)) {
            String outRefundNo = dto.getOrderId() + "_" + RandomUtil.randomNumbers(4);
            RefundOrder refundOrder = new RefundOrder();
            refundOrder.setOrderSn(PayOrderUtil.getOrderSn(PayTypeConstant.Refund));
            refundOrder.setOrderId(paymentOrder.getOrderId());
            refundOrder.setProductName(paymentOrder.getProductName());
            refundOrder.setPayMethod(PayMethodConstant.WECHAT);
            refundOrder.setRefundAmount(ObjectUtil.isEmpty(dto.getRefundAmount()) ? paymentOrder.getPayAmount() : dto.getRefundAmount());
            refundOrder.setRefundStatus(RefundStatusEnum.STATUS_0.getCode());
            refundOrder.setCallbackUrl(commonProperties.getDomainName() + "/refund/notify");
            refundOrder.setClientIp(IpUtil.getClientIp(request));
            refundOrder.setReason(dto.getReason() != null ? dto.getReason() : "");
            refundOrder.setOutRefundNo(outRefundNo);
            log.info("微信退款-请求参数 {}", JSONUtil.toJsonStr(refundOrder));
            boolean res = refundOrderService.save(refundOrder);
            if (res) {
                try {
                    WxPayRefundV3Request refundV3Request = new WxPayRefundV3Request();
                    refundV3Request.setOutRefundNo(refundOrder.getOutRefundNo());
                    refundV3Request.setOutTradeNo(paymentOrder.getOutTradeNo());
                    WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
                    amount.setRefund(refundOrder.getRefundAmount());
                    amount.setTotal(paymentOrder.getPayAmount());
                    amount.setCurrency("CNY");
                    refundV3Request.setAmount(amount);
                    refundV3Request.setNotifyUrl(refundOrder.getCallbackUrl());
                    log.info("微信退款-响应参数 {}", JSONUtil.toJsonStr(refundV3Request));
                    return wxPayService.refundV3(refundV3Request);
                } catch (WxPayException e) {
                    log.error("createOrder errMsg: {}", e.getReturnMsg());
                    throw new BusinessException("创建微信退款订单失败");
                }
            }
        }
        throw new BusinessException("微信支付单号查询失败");
    }

    @Override
    public Map<String, String> notify(HttpServletRequest request, HttpServletResponse response) throws WxPayException, IOException {
        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setTimeStamp(request.getHeader("Wechatpay-Timestamp"));
        signatureHeader.setNonce(request.getHeader("Wechatpay-Nonce"));
        signatureHeader.setSignature(request.getHeader("Wechatpay-Signature"));
        signatureHeader.setSerial(request.getHeader("Wechatpay-Serial"));
        String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
        WxPayRefundNotifyV3Result notifyV3Result = wxPayService.parseRefundNotifyV3Result(xmlResult, signatureHeader);
        log.info("微信退款-回调 {}", JSON.toJSONString(notifyV3Result));

        // 取出订单ID
        String orderId = StringUtils.substringBeforeLast(notifyV3Result.getResult().getOutRefundNo(), "_");

        Map<String, String> resultMap = new HashMap<>();
        RefundOrder refundOrder = refundOrderMapper.getOrderByOrderId(orderId);
        if (ObjectUtil.isNotNull(refundOrder)) {
            if (Objects.equals(refundOrder.getRefundStatus(), RefundStatusEnum.STATUS_1.getCode())) {
                resultMap.put("code", ResultStatusConstant.SUCCESS);
                resultMap.put("message", ResultStatusConstant.SUCCESS);
                return resultMap;
            }
            // 根据退款标识判断是否退款成功
            boolean refundFlag = notifyV3Result.getResult().getRefundStatus().equals(ResultStatusConstant.SUCCESS);
            LambdaUpdateWrapper<RefundOrder> refundUpdate = new LambdaUpdateWrapper<>();
            refundUpdate.set(RefundOrder::getRefundStatus, refundFlag ? RefundStatusEnum.STATUS_1.getCode() : RefundStatusEnum.STATUS_2.getCode());
            refundUpdate.set(RefundOrder::getRefundTime, new Date());
            refundUpdate.eq(RefundOrder::getId, refundOrder.getId());
            boolean res = refundOrderService.update(refundOrder, refundUpdate);
            if (res && refundFlag) {
                Order order = orderService.getById(orderId);
                LambdaUpdateWrapper<Order> orderUpdate = new LambdaUpdateWrapper<>();
                orderUpdate.set(Order::getRefundAmount, new BigDecimal(refundOrder.getRefundAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
                orderUpdate.set(Order::getOrderStatus, OrderStatusEnum.STATUS_4.getCode());
                orderUpdate.eq(Order::getOrderId, order.getOrderId());
                orderService.update(order, orderUpdate);
            }
            resultMap.put("code", ResultStatusConstant.SUCCESS);
            resultMap.put("message", ResultStatusConstant.SUCCESS);
            return resultMap;
        }
        response.setStatus(500);
        resultMap.put("code", ResultStatusConstant.FAIL);
        resultMap.put("message", ResultStatusConstant.FAIL);
        return resultMap;
    }
}
